#include "taskgroup.h"
#include <utility>

namespace yc {

TaskGroup::TaskGroup(int id, bool auto_delete, int timeout, QObject* parent)
    : TaskGroup(1, id, auto_delete, timeout, parent)
{
}

TaskGroup::TaskGroup(int parallel_count, int id, bool auto_delete, int timeout, QObject* parent)
    : Task(id, auto_delete, timeout, parent), parallel_count_(qMax(1, parallel_count))
{
}

TaskGroup::~TaskGroup()
{
    terminateTasks();
}

void TaskGroup::addTask(Task* task, bool at_end)
{
    if (task && !waiting_tasks_.contains(task) && !running_tasks_.contains(task)) {
        connect(task, &Task::finished, this, [this, task] { removeTask(task); });
        connect(task, &Task::failed, this, [this, task](int error, const QString& message) {
            if (ignore_errors_ || keep_running_) {
                removeTask(task);
            } else {
                Q_EMIT failed(error, message);
            }
        });
        connect(task, &Task::destroyed, this, [this, task] { removeTask(task); });
        if (at_end) {
            waiting_tasks_.append(task);
        } else {
            waiting_tasks_.push_front(task);
        }
        if (isRunning()) {
            startNextTask();
        }
    }
}

void TaskGroup::terminateTask(int id)
{
    Task* t = nullptr;
    for (Task* p : std::as_const(running_tasks_)) {
        if (p->id() == id) {
            t = p;
            break;
        }
    }
    if (!t) {
        for (Task* p : std::as_const(waiting_tasks_)) {
            if (p->id() == id) {
                t = p;
                break;
            }
        }
    }
    if (t) {
        disconnect(t, nullptr, this, nullptr);
        t->terminate();
        removeTask(t);
        if (t->autoDelete()) {
            t->deleteLater();
        }
    }
}

void TaskGroup::terminateTasks()
{
    Task* t;
    while (!waiting_tasks_.empty()) {
        t = waiting_tasks_.takeLast();
        disconnect(t, nullptr, this, nullptr);
        t->terminate();
        if (t->autoDelete()) {
            t->deleteLater();
        }
    }
    while (!running_tasks_.empty()) {
        t = running_tasks_.takeLast();
        disconnect(t, nullptr, this, nullptr);
        t->terminate();
        if (t->autoDelete()) {
            t->deleteLater();
        }
    }
}

void TaskGroup::startImpl()
{
    startNextTask();
}

void TaskGroup::terminateImpl()
{
    terminateTasks();
}

void TaskGroup::startNextTask()
{
    if (isRunning()) {
        if (waiting_tasks_.empty() && running_tasks_.empty()) {
            if (!keep_running_) {
                Q_EMIT finished();
            }
        } else {
            if (!waiting_tasks_.empty() && (running_tasks_.size() < parallel_count_)) {
                Task* t = waiting_tasks_.first();
                waiting_tasks_.removeFirst();
                running_tasks_.append(t);
                t->start();
            }
        }
    }
}

void TaskGroup::removeTask(Task* task)
{
    disconnect(task, nullptr, this, nullptr);
    if (running_tasks_.removeOne(task)) {
        startNextTask();
    } else {
        waiting_tasks_.removeOne(task);
    }
}
} // namespace yc
